package com.time.social.controller;

import com.google.common.base.Preconditions;
import com.time.social.config.SocialConfig;
import com.time.social.entity.Result;
import com.time.social.entity.SocialTokenResponse;
import com.time.social.entity.SocialUserInfo;
import com.time.social.enums.SocialTypeEnum;
import com.time.social.redis.RedisUtil;
import com.time.social.service.AuthUserRpcService;
import com.time.social.utils.HttpClientUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.view.RedirectView;

import java.util.UUID;

/**
 * 抽象社交登录控制器基类
 * 使用模板方法模式，定义通用的第三方登录流程
 *
 * @author: HuangQi
 * @date: Created in 2025/01/XX
 * @description: 抽象社交登录控制器基类
 */
@Slf4j
public abstract class AbstractSocialLoginController {

    @Autowired
    protected SocialConfig socialConfig;

    @Autowired
    protected RedisUtil redisUtil;

    @Autowired
    protected AuthUserRpcService authUserRpcService;

    /**
     * State缓存前缀
     */
    private static final String STATE_CACHE_PREFIX = "social:state:";

    /**
     * State缓存过期时间（秒）
     */
    private static final long STATE_EXPIRE_TIME = 600;

    /**
     * 跳转到第三方授权页面
     *
     * @return 重定向视图
     */
    public RedirectView login() {
        try {
            if (log.isInfoEnabled()) {
                log.info("{}.login", getSocialType().getDesc());
            }

            // 生成state参数，防止CSRF攻击
            String state = generateState();
            String authUrl = getAuthUrl(state);

            // 返回重定向视图
            return new RedirectView(authUrl);
        } catch (Exception e) {
            log.error("{}.login.error:{}", getSocialType().getDesc(), e.getMessage(), e);
            throw new RuntimeException("跳转授权页面失败", e);
        }
    }

    /**
     * 处理第三方回调
     *
     * @param code  授权码
     * @param state 状态参数
     * @return 登录结果
     */
    public Result<Object> callback(@RequestParam("code") String code,
                                    @RequestParam(value = "state", required = false) String state) {
        try {
            if (log.isInfoEnabled()) {
                log.info("{}.callback.code:{}, state:{}", getSocialType().getDesc(), code, state);
            }

            // 参数校验
            Preconditions.checkNotNull(code, "授权码不能为空");

            // 验证state（如果提供了state）
            if (state != null && !validateState(state)) {
                return Result.fail("无效的state参数，可能存在CSRF攻击");
            }

            // 1. 使用code换取access_token
            SocialTokenResponse tokenResponse = getAccessToken(code);
            if (tokenResponse == null || tokenResponse.getAccessToken() == null) {
                String error = tokenResponse != null ? tokenResponse.getError() : "未知错误";
                String errorDesc = tokenResponse != null ? tokenResponse.getErrorDescription() : "获取token失败";
                log.error("{}.callback.getToken失败: {}, {}", getSocialType().getDesc(), error, errorDesc);
                return Result.fail("获取访问令牌失败: " + errorDesc);
            }

            // 2. 使用access_token获取用户信息
            SocialUserInfo userInfo = getUserInfo(tokenResponse.getAccessToken());
            if (userInfo == null || userInfo.getOpenId() == null) {
                log.error("{}.callback.getUserInfo失败", getSocialType().getDesc());
                return Result.fail("获取用户信息失败");
            }

            // 设置社交平台类型
            userInfo.setSocialType(getSocialType().getCode());

            // 3. 创建或更新用户，并生成登录token
            String userName = authUserRpcService.createOrUpdateUser(userInfo);
            Object tokenInfo = authUserRpcService.doLogin(userName);

            // 返回登录结果
            return Result.ok(tokenInfo);
        } catch (Exception e) {
            log.error("{}.callback.error:{}", getSocialType().getDesc(), e.getMessage(), e);
            return Result.fail("第三方登录失败: " + e.getMessage());
        }
    }

    /**
     * 生成授权URL（子类实现）
     *
     * @param state 状态参数
     * @return 授权URL
     */
    protected abstract String getAuthUrl(String state);

    /**
     * 使用code换取access_token（子类实现）
     *
     * @param code 授权码
     * @return Token响应
     */
    protected abstract SocialTokenResponse getAccessToken(String code);

    /**
     * 使用access_token获取用户信息（子类实现）
     *
     * @param accessToken 访问令牌
     * @return 用户信息
     */
    protected abstract SocialUserInfo getUserInfo(String accessToken);

    /**
     * 获取社交平台类型（子类实现）
     *
     * @return 社交平台类型枚举
     */
    protected abstract SocialTypeEnum getSocialType();

    /**
     * 构建回调URL
     *
     * @return 回调URL
     */
    protected String buildCallbackUrl() {
        String baseUrl = socialConfig.getCallbackBaseUrl();
        String redirectUri = getRedirectUri();
        return baseUrl + redirectUri;
    }

    /**
     * 获取回调地址（子类实现）
     *
     * @return 回调地址
     */
    protected abstract String getRedirectUri();

    /**
     * 生成state参数
     *
     * @return state值
     */
    private String generateState() {
        String state = getSocialType().getCode() + ":" + UUID.randomUUID().toString();
        String cacheKey = STATE_CACHE_PREFIX + state;
        // 将state存入Redis，有效期10分钟
        redisUtil.set(cacheKey, "1", STATE_EXPIRE_TIME);
        return state;
    }

    /**
     * 验证state参数
     *
     * @param state state值
     * @return 是否有效
     */
    private boolean validateState(String state) {
        String cacheKey = STATE_CACHE_PREFIX + state;
        String value = redisUtil.get(cacheKey);
        if (value != null) {
            // 验证成功后删除state，防止重复使用
            redisUtil.del(cacheKey);
            return true;
        }
        return false;
    }

    /**
     * 发送HTTP GET请求（通用方法）
     *
     * @param url 请求URL
     * @return 响应内容
     */
    protected String httpGet(String url) {
        try {
            return HttpClientUtils.get(url);
        } catch (Exception e) {
            log.error("HTTP GET请求失败: url={}, error={}", url, e.getMessage(), e);
            throw new RuntimeException("HTTP请求失败", e);
        }
    }

    /**
     * 发送HTTP POST请求（通用方法）
     *
     * @param url 请求URL
     * @return 响应内容
     */
    protected String httpPost(String url) {
        try {
            return HttpClientUtils.post(url, null, "application/json", "UTF-8", 10000, 10000);
        } catch (Exception e) {
            log.error("HTTP POST请求失败: url={}, error={}", url, e.getMessage(), e);
            throw new RuntimeException("HTTP请求失败", e);
        }
    }

    /**
     * 发送HTTP POST表单请求（通用方法）
     *
     * @param url 请求URL
     * @param params 表单参数
     * @return 响应内容
     */
    protected String httpPostForm(String url, java.util.Map<String, String> params) {
        try {
            return HttpClientUtils.postParameters(url, params);
        } catch (Exception e) {
            log.error("HTTP POST表单请求失败: url={}, error={}", url, e.getMessage(), e);
            throw new RuntimeException("HTTP请求失败", e);
        }
    }
}

